PICターゲット・ボードのチェック編 その2
(LEDをON−OFF点滅させる)
LEDをON−OFF点滅させてボードをチェックしてみよう!
RAポート:全LED点滅動作
チェック編その1では、RA4ピンをプルアップしてしまうと、プログラムを動作させる前(イレースされている状態のPIC)で、RA4ピンがすでに点灯してしまっていて、点灯させるだけのプログラムでは、動作前後でLEDの変化が見られずチェックができませんでした。そこで、次にLEDをON−OFF点滅させることによって、その変化をみることにしましょう。
さて、点滅させる場合には、その点滅間隔をまず決めてやる必要があります。ここでは1秒間隔で点滅させるプログラムを考えてみましょう。(点滅があまりにも速すぎて判断できないと困りますし、逆に遅すぎてもイライラしますので・・・)
【LEDの間隔を決める遅延サブルーチン】
出力ポートをONにする → ループするプログラムで一定時間待つ → 出力ポートをOFFにする → ループするプログラムで一定時間待つ → 出力ポートをONにする → ・・・
と繰り返す方法で、命令で時間を確保しながら動作させてみましょう。
一定時間待つプログラムは、あらかじめ命令の実行ステップ数を勘定することで時間を計算します。
一定時間待つプログラムは、あらかじめ命令の実行ステップ数を勘定することで時間を計算します。PIC命令の1サイクルの実行時間は、
1サイクル実行時間=4/クロック周波数
で計算します。たとえば、クロック周波数が20MHz(0.05μs)の場合は、
4/20MHz=4*0.05μs=0.2μs
したがって、1サイクルは、0.2μsの実行時間を必要とします。
ただし、下記の命令だけは2サイクル必要とするので、倍の実行時間となります。
命令 | 意味 |
GOTO | 指定番地にジャンプする。 |
CALL | サブルーチンにジャンプする。 |
RETURN | サブルーチンから無条件復帰する。 |
RETFIE | 割り込みから復帰する。復帰時に割り込みを許可する。 |
RETLW | リテラルデータをWregにセットして復帰する。 |
BTFSC | fレジスタの第bビット目が0だったら次の命令をスキップ 1サイクルだが、スキップするとき2サイクル |
BTFSS | fレジスタの第bビット目が1だったら次の命令をスキップ 1サイクルだが、スキップするとき2サイクル |
DECFSZ | fレジスタの減算をし、結果が0なら次の命令をスキップ 1サイクルだが、スキップするとき2サイクル |
INCFSZ | fレジスタの加算をし、結果が0なら次の命令をスキップ 1サイクルだが、スキップするとき2サイクル |
TIME100 MOVLW 0A5H MOVWF COUNT NOP NOP LOOP DECFSZ COUNT,F GOTO LOOP RETURN |
; 100μs遅延サブルーチン(20MHzクロック時) ;(1)1サイクル A5H=165 ;(2)1サイクル ;(3)1サイクル微調整ダミー ;(4)1サイクル微調整ダミー ;(5)1×(165-1)+2 = 166サイクル ;(6)2×(165-1) = 328サイクル ;(7)2サイクル ; 計500サイクル×0.2μs=100μs |
【100μs遅延サブルーチンの説明】
(1)ループ回数(0A5H=165回)をWregにロードする。
(2)Wregの内容をfレジスタ(COUNT)に入れる
(3)(4)サイクル数をちょうど500にするために、
1サイクル分のNOP命令(何もしないダミー)を入れた。
(5)fレジスタ(COUNT)の減算をし、結果が0なら次の命令をスキップする。
0以外である(165−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(6)(165−1)回分は、2サイクル
(7)サブルーチンから無条件復帰するとき、2サイクル
よって、計500サイクルになり、20MHzクロックでは、1サイクル0.2μsであることから、100μsになります。
(B)10ms遅延サブルーチンの作り方(例)
TIME10M MOVLW 63H MOVWF COUNT1 NOP NOP LOOP1 CALL TIME100 DECFSZ COUNT1,F GOTO LOOP1 RETURN |
; 10ms遅延サブルーチン(20MHzクロック時) ;(1)1サイクル 63H=99 ;(2)1サイクル ;(3)1サイクル 微調整ダミー ;(4)1サイクル 微調整ダミー ;(5)(2+500)×99=49698サイクル ;(6)1×(99-1)+2 = 100サイクル ;(7)2×(99-1) = 196サイクル ;(8)2サイクル ; 計50000サイクル×0.2μs=10ms |
【10ms遅延サブルーチンの説明】
(1)ループ回数(63H=99回)をWregにロードする。
(2)Wregの内容をfレジスタ(COUNT1)に入れる
(3)(4)サイクル数をちょうど50000にするために、
1サイクル分のNOP命令(何もしないダミー)を入れた。
(5)前述の100μsサブルーチン(500サイクル)を利用する。
サブルーチンにジャンプするとき、2サイクルで、99回ループする。
よって、(2+500)×99=49698サイクル
(6)fレジスタ(COUNT1)の減算をし、結果が0なら次の命令をスキップする。
0以外である(99−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(6)(99−1)回分は、2サイクル
(7)サブルーチンから無条件復帰するとき、2サイクル
よって、計50000サイクルになり、20MHzクロックでは、1サイクル0.2μsであることから、10msになります。
(C)1s遅延サブルーチンの作り方(例)
TIME1S MOVLW 63H MOVWF COUNT2 MOVLW 62H MOVWF COUNT3 MOVLW 03H MOVWF COUNT4 NOP LOOP2 CALL TIME10M DECFSZ COUNT2,F GOTO LOOP2 LOOP3 CALL TIME100 DECFSZ COUNT3,F GOTO LOOP2 LOOP4 DECFSZ COUNT4,F GOTO LOOP4 RETURN |
;1s遅延サブルーチン(20MHzクロック時) ;(1)1サイクル 63H=99 ;(2)1サイクル ;(3)1サイクル 62H=98 ;(4)1サイクル ;(5)1サイクル 03H=3 ;(6)1サイクル ;(7)1サイクル 微調整ダミー ; ;(8)(2+50000)×99=4950198サイクル ;(9)1×(99-1)+2 = 100サイクル ;(10)2×(99-1) = 196サイクル ; ;(11)(2+500)×98=49196 ;(12)1×(98-1)+2 = 99サイクル ;(13)2×(98-1) = 194サイクル ; ;(14)1×(3-1)+2 = 4サイクル ;(15)2×(3-1) = 4サイクル ;(16)2サイクル ; 計5000000サイクル×0.2μs=1s |
【1s遅延サブルーチンの説明】
(1)ループ回数(63H=99回)をWregにロードする。
(2)Wregの内容をfレジスタ(COUNT2)に入れる
(3)ループ回数(62H=98回)をWregにロードする。
(4)Wregの内容をfレジスタ(COUNT3)に入れる
(5)ループ回数(03H=3回)をWregにロードする。
(6)Wregの内容をfレジスタ(COUNT4)に入れる
(7)サイクル数をちょうど5000000にするために、
1サイクル分のNOP命令(何もしないダミー)を入れた。
(8)前述の10msサブルーチン(50000サイクル)を利用する。
サブルーチンにジャンプするとき、2サイクルで、99回ループする。
よって、(2+50000)×99=4950198サイクル
(9)fレジスタ(COUNT2)の減算をし、結果が0なら次の命令をスキップする。
0以外である(99−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(10)(99−1)回分は、2サイクル
(11)前述の100μsサブルーチン(500サイクル)を利用する。
サブルーチンにジャンプするとき、2サイクルで、98回ループする。
よって、(2+500)×99=49196サイクル
(12)fレジスタ(COUNT3)の減算をし、結果が0なら次の命令をスキップする。
0以外である(98−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(13)(98−1)回分は、2サイクル
(14)fレジスタ(COUNT4)の減算をし、結果が0なら次の命令をスキップする。
0以外である(3−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(15)(3−1)回分は、2サイクル
(16)サブルーチンから無条件復帰するとき、2サイクル
よって、計5000000サイクルになり、20MHzクロックでは、1サイクル0.2μsであることから、1sになります。
RAポートの全LED点滅動作について、以下にプログラムリストを示します。
(注)以下に示すプログラムには、ホームページ画面作成の都合上、空白として全角文字のスペースなどが挿入されています。したがって、下記プログラムリストをそのままコピーしてMPLABのソースファイルとされた場合には、エラーとなることがあります。
→ここをクリックして、下記のプログラムをダウンロードするようにしてください。
ファイル名:「tenmetu.asm」 サイズ2.84kバイト
→ここをクリックして、下記のオブジェクトファイルをダウンロードするようにしてください。
ファイル名:「tenmetu.hex」 サイズ255バイト
;*********************************************************** ; Port A 動作チェックプログラム(2) ; Port A の発光ダイオードを点滅させる ; 全ポートを出力させるためには、 ; RA4ポートがプルアップされていること ;*********************************************************** |
|
LIST P=PIC16F877 INCLUDE P16F877.INC |
; (1)プロセッサの種別指定 ; (2)インクルードファイルの指定 |
;*********************************************************** ; 変数定義とレジスタ割付 ;*********************************************************** |
|
COUNT EQU 20H COUNT1 EQU 21H COUNT2 EQU 22H COUNT3 EQU 23H COUNT4 EQU 24H ORG 0 |
;(3)ループカウンタ ;(3)ループカウンタ1 ;(3)ループカウンタ2 ;(3)ループカウンタ3 ;(3)ループカウンタ4 ; ;(4)プログラムの開始番地の指定 |
;*********************************************************** ; 入出力ピン初期化 ;*********************************************************** |
|
BSF STATUS,RP0 CLRF TRISA BCF STATUS,RP0 |
;(5)Bank 1 へ切替 ;(6)PortA 全ポートを出力に設定 ;(7)Bank 0 へ戻る |
;*********************************************************** ; メインプログラム ;*********************************************************** |
|
MAINLP MOVLW 3FH MOVWF PORTA CALL TIME1S MOVLW 00H MOVWF PORTA CALL TIME1S GOTO MAINLP |
;(8)ラベル ;(9)00111111 を Wreg にロードする ;(10)PORTAへWregのデータを出力 ; 出力ポートをONにする ;(11)1秒一定時間待つ ;(12)00000000 を Wreg にロードする ;(13)PORTAへWregのデータを出力 ; 出力ポートをOFFにする ;(14)1秒一定時間待つ ;(15)指定ラベルにジャンプする |
;*********** サブルーチン群 ****************************** ; ;100μs遅延サブルーチン(20MHzクロック時) ;・・・・・上記プログラムリスト参照・・・・・ ; ;10ms遅延サブルーチン(20MHzクロック時) ;・・・・・上記プログラムリスト参照・・・・・ ; ;1s遅延サブルーチン(20MHzクロック時) ;・・・・・上記プログラムリスト参照・・・・・ |
|
END | ;(16)終了 |
【プログラムの説明】
このプログラムについて、順を追って解説を加えておきましょう。
(1)プロセッサの種別指定
定義の仕方は「PROCESSOR」か「LIST」命令を使って設定します。ここで指定するプロセッサ名称は、パッケージの種類を示すサフィックス(最後の英記号の部分)は不要です。
PROCESSOR PIC16F877
または
LIST P=PIC16F877
(2)標準ヘッダーファイルのインクルード
標準ヘッダーファイルとは、各プロセッサが持っているSFR(Special
Function Register)をラベル(記号)で使える様にするため、ラベルとハードウェアの場所とを定義しているファイルです。標準ヘッダーファイルは 「プロセッサ名.INC」というファイル名で統一されて、MPLABのディレクトリに格納されています。従って、これのインクルード方法は下記のようにして行います。
一度、参考までに標準ヘッダーファイルの内容をエディタ等で見ておくことをお勧めします。
INCLUDE P16F877.INC
または
#INCLUDE P16F877.INC
(3)変数定義とレジスタ割付
レジスタファイルアドレスを指定するときに、アドレス数値を直接指定することもできますが、数値だけでは間違いも多く、プログラム自身も分かりにくくなってしまいます。そこで、EQU命令などを使ってラベルを設定し変数を定義します。レジスタファイルアドレスは、7ビットあるので、00〜7Fまで最大128個のレジスタが指定できますが、実際に物理的に実装されて汎用的に使用できるレジスタ数はデバイスによって異なっていますので注意が必要です。
PIC16F84の汎用レジスタのアドレスは、 0C〜4F
PIC16F877の汎用レジスタのアドレスは、20〜7F
となっています。したがって、PIC16F84で作成されたプログラムをPIC16F877へ移植する場合には、0C〜1Fの間でレジスタを割り付けられている箇所は変更が必要です。
(4)プログラムの開始番地の指定
「ORG」はプログラムの開始番地を指定する擬似命令で、ORG以下の実際のプログラム命令が格納されるプログラムメモリ内の位置(アドレス)を指定します。
ORG 0 ;0番地から格納することを示します。
コンピュータは一般に電源投入時やリセットをすると必ず0番地からスタートするようになっているので、0番地には必ず命令があることが必要です。
(5)Bank 1 へ切替
PICには各種の動作モードを設定するための Special
Register と呼ばれるものが用意されています。PICを動作させるためには、まずこのSpecial
Registerの設定から始めます。そしてそれらは全て、Register
File と呼ぶメモリとして用意されています。その
Register Fileは Bank0, Bank1, Bank2, Bank3
とよばれるアドレス空間をもっているため、多少アクセスの仕方が面倒です。つまりRESET後の通常はBank0となっているので、Bank1側のレジスタにアクセスするときはBankの切替えをしてからとなります。またBank0とBank1に同じ物があるときにはどちらでも同じ様に使えます。
Bank1へ切り替える方法ですが、「STATUS」レジスタにある2ビットのRP0、RP1を変えてBankを指定します。デフォルトは、Bank0です。表1にBankとRP1,RP0ビットとの関係を示します。Bank1へ切り替えるためには、RP0ビットを「1」にします。(RP1はデフォルトで「0」なので変える必要はない。)
BSF STATUS,RP0
「STATUS」レジスタのRP0ビットを「1」にする。
Bank | RP1 | RP0 |
0 | 0 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
(6)PortA 全ポートを出力に設定
PIC16F877には、I/Oポートが5組用意されています。入出力モードの設定は、それぞれのI/Oポートのピン1本1本を独立して入力か出力かプログラムで決めることができ、TRISA,
TRISB, TRISC, TRISD, TRISE という特別なレジスタ(SRF)に設定します。このTRISxレジスタの各ビットが、対応する各ポートPORTxの同じ位置のビットの入出力モードを決定します。PORTxのビットを出力に設定する時には、TRISxの対応するビットを「0」に設定します。入力に設定する時には、「1」に設定しますが、電源ON後やRESET後はすべて「入力」として設定されています。
それでは、PortAの全ポートを出力に設定します。Bank1にあるTRISAレジスタを変更して出力に設定します。TRISAレジスタは、CLRF(fレジスタをゼロクリアする命令)で出力設定となります。
CLRF TRISA
「TRISAレジスタをゼロクリアする。すなわち、PORTA全ポートを出力に設定する」
(7)Bank 0 へ戻す
動作モードの設定後は、Bank0に戻しておきます。Bank1へ切り替えるためには、RP0ビットを「0」にします。(RP1はデフォルトで「0」なので変える必要はない。)
BCF STATUS,RP0
「STATUS」レジスタのRP0ビットを「0」にする。すなわち、Bank0に戻す。」
(8)ラベル
ラベルは各行の先頭に記載しなければなりません。また含まれる文字は、アルファベットかアンダーバー(_)で始まる英数字でなければなりません。ラベルの長さは、32文字以下までです。大文字、小文字の区別がありますが、MPLAB側の環境設定でなくすことができます。
(9)00111111を Wreg にロード
RA0〜5の全ポートを「H」出力するため、00111111を (すなわち16進表示で、3F)を、Wregにロードする。
MOVLW 3FH
「00111111をWregにロードする」
(10)PORTへWregのデータを出力
実際に出力するには、MOVWFなどの転送命令や、BSFなどのビット操作命令を使います。
MOVWF PORTA
「PORTAへWregのデータを出力し、LEDをONする」
(11)1秒一定時間待つ
メインとなるルーチンから、「CALL」命令を使って1秒一定時間待つサブルーチンを呼び出します。呼び出しの仕方は、単にサブルーチンの入り口となる場所に付けたラベル名がサブルーチン名となりますので、それをCALLするだけです。
CALL TIME1S
「1秒一定時間待つ TIME1Sサブルーチンを呼び出す。」
(12)00000000を Wreg にロード
RA0〜5の全ポートを「L」出力するため、00000000を (すなわち16進表示で、00H)を、Wregにロードする。
MOVLW 00H
「00000000をWregにロードする」
(13)PORTへWregのデータを出力
実際に出力するには、MOVWFなどの転送命令や、BSFなどのビット操作命令を使います。
MOVWF PORTA
「PORTAへWregのデータを出力し、LEDをOFFする」
(14)1秒一定時間待つ
CALL TIME1S
「1秒一定時間待つ TIME1Sサブルーチンを呼び出す。」
(15)指定ラベルへジャンプする
GOTO MAINLP
「MAINLPとつけたラベルにジャンプする」
したがって、MAINLP間を無限に繰り返すことになり、LEDが1秒間間隔でON−OFFする。
(16)終了
「END」はソースの終わりを示す必ず必要な擬似命令で、アセンブラに対し、ここがソースの終わりであることを知らせる役割があります。
END
「ソースプログラムの終わりであることを示す。」
図1にRAポートのLED点滅動作の様子を示します。
![]() |
図1.RAポートのLED点滅動作の様子 |
RAポート:LEDを交互に点滅動作
RA4ポートのLED点滅動作ですが、それを少し変更して今度は交互に点滅させてみましょう。
Wreg にロードするところで、
MOVLW 3FH ;(9)00111111を Wreg にロードする
となっていた箇所を
MOVLW 2AH ;(9)00101010を Wreg にロードする
として設定します。
また、
MOVLW 00H ;(12)00000000 を Wreg
にロードする
となっていた箇所を
MOVLW 15H ;(12)00010101を Wreg
にロードする
として設定します。
RAポートのLEDを交互に点滅させるプログラムリストを示します。
→ここをクリックして、下記のプログラムをダウンロードしてください。
ファイル名:「kougo_a.asm」 サイズ2.79kバイト
→ここをクリックして、下記のオブジェクトファイルをダウンロードするようにしてください。
ファイル名:「kougo_a.hex」 サイズ255バイト
図2にRAポートのLEDを交互に点滅させた様子を示します。
![]() |
図2.RAポートのLEDを交互に点滅させた様子 |
他のポート:LEDを交互に点滅動作
RA4ポートのLED点滅動作ですが、それを他のポートに変更して今度は交互に点滅させてみましょう。たとえば、RBポートに設定する場合を考えてみます。
ポートの設定では、
CLRF TRISA ;(6)PortA 全ポートを出力に設定
となっていた箇所を
CLRF TRISB ;(6)PortB 全ポートを出力に設定
として、RCポートに設定します。
また、出力データを
MOVWF PORTA ;(10)PORTBへWregのデータを出力
となっていた箇所を
MOVWF PORTB ;(10)PORTCへWregのデータを出力
とします。(13)についても同様です。
また、RAとRBポートではピン数が違いますのでWreg
にロードするところで、
MOVLW 3FH ;(9)00111111を Wreg にロードする
となっていた箇所を
MOVLW 0AAH ;(9)10101010を Wreg にロードする
として設定します。
また、
MOVLW 00H ;(12)00000000 を Wreg
にロードする
となっていた箇所を
MOVLW 55H ;(12)01010101を Wreg
にロードする
として設定します。
RBポートのLEDを交互に点滅させるプログラムリストを示します。
→ここをクリックして、下記のプログラムをダウンロードしてください。
ファイル名:「kougo_b.asm」 サイズ2.71kバイト
→ここをクリックして、下記のオブジェクトファイルをダウンロードするようにしてください。
ファイル名:「kougo_b.hex」 サイズ255バイト
![]() |
|
![]() |